package com.itcam.daijia.driver.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.itcam.daijia.common.constant.SystemConstant;
import com.itcam.daijia.common.execption.NGException;
import com.itcam.daijia.common.result.ResultCodeEnum;
import com.itcam.daijia.driver.config.TencentCloudProperties;
import com.itcam.daijia.driver.mapper.*;
import com.itcam.daijia.driver.service.CosService;
import com.itcam.daijia.driver.service.DriverInfoService;
import com.itcam.daijia.model.entity.driver.*;
import com.itcam.daijia.model.form.driver.DriverFaceModelForm;
import com.itcam.daijia.model.form.driver.UpdateDriverAuthInfoForm;
import com.itcam.daijia.model.vo.driver.DriverAuthInfoVo;
import com.itcam.daijia.model.vo.driver.DriverInfoVo;
import com.itcam.daijia.model.vo.driver.DriverLoginVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tencentcloudapi.common.AbstractModel;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.iai.v20200303.IaiClient;
import com.tencentcloudapi.iai.v20200303.models.*;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.sql.Driver;
import java.util.Date;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class DriverInfoServiceImpl extends ServiceImpl<DriverInfoMapper, DriverInfo> implements DriverInfoService {

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private DriverInfoMapper driverInfoMapper;

    @Autowired
    private DriverSetMapper driverSetMapper;

    @Autowired
    private DriverAccountMapper driverAccountMapper;

    @Autowired
    private DriverLoginLogMapper driverLoginLogMapper;

    @Autowired
    private DriverFaceRecognitionMapper driverFaceRecognitionMapper;

    @Autowired
    private CosService cosService;

    @Autowired
    private TencentCloudProperties tencentCloudProperties;


    /**
     * 小程序授权登录
     *
     * @param code 小程序登录时获取的 code
     * @return 返回登录结果，包含 token
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long login(String code) {
        // 初始化 openId 为空
        String openId = null;
        try {
            // 通过微信小程序服务获取用户的会话信息
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
            // 从会话信息中获取 openId
            openId = sessionInfo.getOpenid();
            // 记录日志，输出获取到的 openId
            log.info("【小程序授权】openid={}", openId);
        } catch (WxErrorException e) {
            // 捕获 WxErrorException 异常，并重新抛出一个运行时异常
            throw new RuntimeException(e);
        }

        // 根据 openId 查询司机信息
        DriverInfo driverInfo = this.getOne(new LambdaQueryWrapper<DriverInfo>().eq(DriverInfo::getWxOpenId, openId));
        if (null == driverInfo) {
            // 如果司机信息不存在，则创建一个新的司机信息
            driverInfo = new DriverInfo();
            // 设置司机的昵称
            driverInfo.setNickname(String.valueOf(System.currentTimeMillis()));
            // 设置司机的头像链接为空
            driverInfo.setAvatarUrl("");
            // 设置司机的微信 openId
            driverInfo.setWxOpenId(openId);
            // 保存司机信息到数据库
            this.save(driverInfo);

            // 初始化默认设置
            DriverSet driverSet = new DriverSet();
            // 设置司机的 ID
            driverSet.setDriverId(driverInfo.getId());
            // 设置订单距离为无限制
            driverSet.setOrderDistance(new BigDecimal(0));
            // 设置默认接单范围为 5 公里
            driverSet.setAcceptDistance(new BigDecimal(SystemConstant.ACCEPT_DISTANCE));
            // 设置不自动接单
            driverSet.setIsAutoAccept(0);
            // 插入司机设置到数据库
            driverSetMapper.insert(driverSet);

            // 初始化司机账户
            DriverAccount driverAccount = new DriverAccount();
            // 设置司机的 ID
            driverAccount.setDriverId(driverInfo.getId());
            // 插入司机账户到数据库
            driverAccountMapper.insert(driverAccount);
        }

        // 记录登录日志
        DriverLoginLog driverLoginLog = new DriverLoginLog();
        // 设置司机的 ID
        driverLoginLog.setDriverId(driverInfo.getId());
        // 设置登录信息
        driverLoginLog.setMsg("小程序登录");
        // 插入登录日志到数据库
        driverLoginLogMapper.insert(driverLoginLog);
        // 返回司机的 ID
        return driverInfo.getId();
    }

    /**
     * 获取司机登录信息
     * @param driverId 司机ID
     * @return 返回司机登录信息
     */
    @Override
    public DriverLoginVo getDriverLoginInfo(Long driverId) {
        // 根据司机ID获取司机信息
        DriverInfo driverInfo = this.getById(driverId);
        // 创建一个新的DriverLoginVo对象
        DriverLoginVo driverLoginVo = new DriverLoginVo();
        // 将司机信息复制到DriverLoginVo对象中
        BeanUtils.copyProperties(driverInfo, driverLoginVo);
        // 判断司机是否创建了人脸库人员，用于接单时做人脸识别判断
        boolean isArchiveFace = StringUtils.hasText(driverInfo.getFaceModelId());
        // 设置DriverLoginVo对象的isArchiveFace属性
        driverLoginVo.setIsArchiveFace(isArchiveFace);
        // 返回司机登录信息
        return driverLoginVo;
    }

    /**
     * 获取司机认证信息
     * @param driverId 司机ID
     * @return 返回司机认证信息
     */
    @Override
    public DriverAuthInfoVo getDriverAuthInfo(Long driverId) {
        // 根据司机ID获取司机信息
        DriverInfo driverInfo = this.getById(driverId);
        // 创建一个新的DriverAuthInfoVo对象
        DriverAuthInfoVo driverAuthInfoVo = new DriverAuthInfoVo();
        // 将司机信息复制到DriverAuthInfoVo对象中
        BeanUtils.copyProperties(driverInfo, driverAuthInfoVo);
        // 设置身份证背面图片的展示URL
        driverAuthInfoVo.setIdcardBackShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardBackUrl()));
        // 设置身份证正面图片的展示URL
        driverAuthInfoVo.setIdcardFrontShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardFrontUrl()));
        // 设置手持身份证图片的展示URL
        driverAuthInfoVo.setIdcardHandShowUrl(cosService.getImageUrl(driverAuthInfoVo.getDriverLicenseFrontUrl()));
        // 设置驾驶证正面图片的展示URL
        driverAuthInfoVo.setDriverLicenseFrontShowUrl(cosService.getImageUrl(driverAuthInfoVo.getDriverLicenseFrontUrl()));
        // 设置驾驶证背面图片的展示URL
        driverAuthInfoVo.setDriverLicenseBackShowUrl(cosService.getImageUrl(driverAuthInfoVo.getDriverLicenseBackUrl()));
        // 返回司机认证信息
        return driverAuthInfoVo;
    }

    /**
     * 更新司机认证信息
     * @param updateDriverAuthInfoForm 更新司机认证信息表单
     * @return 返回更新结果
     */
    @Override
    public Boolean updateDriverAuthInfo(UpdateDriverAuthInfoForm updateDriverAuthInfoForm) {
        // 创建一个新的DriverInfo对象
        DriverInfo driverInfo = new DriverInfo();
        // 设置司机ID
        driverInfo.setId(updateDriverAuthInfoForm.getDriverId());
        // 将表单中的属性复制到司机信息对象中
        BeanUtils.copyProperties(updateDriverAuthInfoForm, driverInfo);
        // 更新司机信息
        return this.updateById(driverInfo);
    }

    /**
     * 创建司机人脸模型
     * @param driverFaceModelForm
     * @return
     */
    @Override
    public Boolean creatDriverFaceModel(DriverFaceModelForm driverFaceModelForm) {
        DriverInfo driverInfo = this.getById(driverFaceModelForm.getDriverId());
        try {
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey());
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            IaiClient client = new IaiClient(cred, tencentCloudProperties.getRegion(), clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            CreatePersonRequest req = new CreatePersonRequest();
            req.setGroupId(tencentCloudProperties.getPersionGroupId());
            //基本信息
            req.setPersonId(String.valueOf(driverInfo.getId()));
            req.setGender(Long.parseLong(driverInfo.getGender()));
            req.setQualityControl(4L);
            req.setUniquePersonControl(4L);
            req.setPersonName(driverInfo.getName());
            req.setImage(driverFaceModelForm.getImageBase64());

            // 返回的resp是一个CreatePersonResponse的实例，与请求对象对应
            CreatePersonResponse resp = client.CreatePerson(req);
            // 输出json格式的字符串回包
            System.out.println(CreatePersonResponse.toJsonString(resp));
            if (StringUtils.hasText(resp.getFaceId())) {
                //人脸校验必要参数，保存到数据库表
                driverInfo.setFaceModelId(resp.getFaceId());
                this.updateById(driverInfo);
            }
        } catch (TencentCloudSDKException e) {
            System.out.println(e.toString());
            return false;
        }
        return true;
    }

    // ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

    /**
     * 获取司机设置信息
     * @param driverId 司机ID
     * @return 返回司机设置信息
     */
    @Override
    public DriverSet getDriverSet(Long driverId) {
        // 创建一个LambdaQueryWrapper对象，用于构建查询条件
        LambdaQueryWrapper<DriverSet> queryWrapper = new LambdaQueryWrapper<>();
        // 添加查询条件，司机id等于传入的driverId
        queryWrapper.eq(DriverSet::getDriverId, driverId);
        // 调用driverSetMapper的selectOne方法，根据查询条件查询司机设置信息
        return driverSetMapper.selectOne(queryWrapper);
    }

    /**
     * 判断司机当日是否进行过人脸识别
     * @param driverId 司机ID
     * @return 如果进行过人脸识别则返回true，否则返回false
     */
    @Override
    public Boolean isFaceRecognition(Long driverId) {
        // 创建一个LambdaQueryWrapper对象，用于构建查询条件
        LambdaQueryWrapper<DriverFaceRecognition> queryWrapper = new LambdaQueryWrapper<>();
        // 添加查询条件，司机id等于传入的driverId
        queryWrapper.eq(DriverFaceRecognition::getDriverId, driverId);
        // 添加查询条件，人脸识别日期等于当天的日期
        queryWrapper.eq(DriverFaceRecognition::getFaceDate, new DateTime().toString("yyyy-MM-dd"));
        // 调用driverFaceRecognitionMapper的selectCount方法，查询符合条件的记录数
        Long count = driverFaceRecognitionMapper.selectCount(queryWrapper);
        // 如果记录数不为0，则表示进行过人脸识别，返回true；否则返回false
        return count != 0;
    }

    /**
     * 人脸验证
     * 文档地址：
     * https://cloud.tencent.com/document/api/867/44983
     * https://console.cloud.tencent.com/api/explorer?Product=iai&Version=2020-03-03&Action=VerifyFace
     *
     * @param driverFaceModelForm
     * @return
     */
    @Override
    public Boolean verifyDriverFace(DriverFaceModelForm driverFaceModelForm) {
        try {
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey());
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象，clientProfile是可选的
            IaiClient client = new IaiClient(cred, tencentCloudProperties.getRegion());
            // 实例化一个请求对象，每个接口都会对应一个request对象
            VerifyFaceRequest req = new VerifyFaceRequest();
            req.setImage(driverFaceModelForm.getImageBase64());
            req.setPersonId(String.valueOf(driverFaceModelForm.getDriverId()));
            // 返回的resp是一个请求对象。每个接口都会对应一个request对象
            VerifyFaceResponse resp = new VerifyFaceResponse();
            // 输出json格式的字符串回包
            System.out.println(VerifyFaceRequest.toJsonString(resp));
            if (resp.getIsMatch()) {
                // 活体检查
                if (this.detectLiveFace(driverFaceModelForm.getImageBase64())) {
                    DriverFaceRecognition driverFaceRecognition = new DriverFaceRecognition();
                    driverFaceRecognition.setDriverId(driverFaceModelForm.getDriverId());
                    driverFaceRecognition.setFaceDate(new Date());
                    driverFaceRecognitionMapper.insert(driverFaceRecognition);
                    return true;
                }
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        }
        throw new NGException(ResultCodeEnum.FACE_ERROR);
    }

    /**
     * 更新接单状态
     * @param driverId 司机ID
     * @param status 状态
     * @return 是否更新成功
     */
    @Override
    public Boolean updateServiceStatus(Long driverId, Integer status) {
        // 创建一个LambdaQueryWrapper对象，用于构建查询条件
        LambdaQueryWrapper<DriverSet> queryWrapper = new LambdaQueryWrapper<>();
        // 添加查询条件，司机id等于传入的driverId
        queryWrapper.eq(DriverSet::getDriverId, driverId);
        // 创建一个DriverSet对象，用于更新数据库中的数据
        DriverSet driverSet = new DriverSet();
        // 设置服务状态为传入的status
        driverSet.setServiceStatus(status);
        // 调用driverSetMapper的update方法，根据查询条件更新司机设置信息
        driverSetMapper.update(driverSet, queryWrapper);
        // 返回true表示更新成功
        return true;
    }

    /**
     * 获取司机基本信息
     * @param driverId
     * @return
     */
    @Override
    public DriverInfoVo getDriverInfo(Long driverId) {
        DriverInfo driverInfo = this.getById(driverId);
        DriverInfoVo driverInfoVo = new DriverInfoVo();
        BeanUtils.copyProperties(driverInfo, driverInfoVo);
        // 驾龄
        Integer driverLicenseAge = new DateTime().getYear() - new DateTime(driverInfo.getDriverLicenseIssueDate()).getYear() + 1;
        driverInfoVo.setDriverLicenseAge(driverLicenseAge);
        return driverInfoVo;
    }

    /**
     * 获取司机OpenId
     * @param driverId
     * @return
     */
    @Override
    public String getDriverOpenId(Long driverId) {
        DriverInfo driverInfo = this.getOne(new LambdaQueryWrapper<DriverInfo>().eq(DriverInfo::getId, driverId).select(DriverInfo::getWxOpenId));
        return driverInfo.getWxOpenId();
    }


    // ____________________________________________________________________________________________________________________________

    /**
     * 人脸静态活体检测
     * 文档地址：
     * https://cloud.tencent.com/document/api/867/48501
     * https://console.cloud.tencent.com/api/explorer?Product=iai&Version=2020-03-03&Action=DetectLiveFace
     * @param imageBase64
     * @return
     */
    private boolean detectLiveFace(String imageBase64) {
        try {
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey());
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象，clientProfile是可选的
            IaiClient client = new IaiClient(cred, tencentCloudProperties.getRegion());
            // 实例化一个请求对象，每个接口都会对应一个request对象
            DetectLiveFaceRequest req = new DetectLiveFaceRequest();
            req.setImage(imageBase64);
            // 返回的resp是一个请求对象。每个接口都会对应一个request对象
            DetectLiveFaceResponse resp = client.DetectLiveFace(req);
            // 输出json格式的字符串回包
            System.out.println(VerifyFaceRequest.toJsonString(resp));
            if (resp.getIsLiveness()) {
                return true;
            }
        } catch (TencentCloudSDKException e) {
            System.out.println(e.toString());
        }
        return false;
    }


}